<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Model {
	
	protected $pk;
	protected $table;
	protected $dbpre;
	protected $linker = null;
	protected $other_join = null;
	protected $tbl_name = null;
	protected $_db;
	protected $dbname = 'default';
	
	public function __construct(){
		$params = config_item($this->dbname,'database');
		if (!is_array($params)) {
			show_error('Your database config file does not appear to be formatted correctly.');
		}
		$params['charset']     = (isset($params['charset']) && $params['charset']) ? $params['charset'] : 'utf8';
		$this->dbpre          = (isset($params['dbprefix']) && $params['dbprefix']) ? $params['dbprefix'] : '';
		if(null == $this->tbl_name)$this->tbl_name = $this->dbpre . $this->table;
		$this->_db              = Model::factory($params);
	}
	
	public function truncate() {
		$this->_db->query("TRUNCATE {$this->tbl_name}");
	}
	
	public function enableLinker($key){
		$linker = $this->linker;
		$linker[$key]['enabled'] = true;
		$linker[$key]['condition'] = false;
		$this->linker = $linker;
	}
	
	public function find($conditions = null, $sort = null, $fields = null){
		if( $record = $this->findAll($conditions, $sort, $fields, 1) ){
			return array_pop($record);
		}else{
			return FALSE;
		}
	}
	
	public function findAll($conditions = null, $sort = null, $fields = null, $limit = null){
		$sql = $this->findAllSql($conditions,$sort,$fields,$limit);
		return $this->_db->getArray($sql);
	}
	
	public function findAllSql($conditions = null, $sort = null, $fields = null, $limit = null){
		$where = "";
		$fields = empty($fields) ? "*" : $fields;
		if(is_array($conditions)){
			$join = array();
			foreach( $conditions as $key => $condition ){
				$condition = $this->escape($condition);
				$join[] = "{$key} = {$condition}";
			}
			$where = "WHERE ".join(" AND ",$join);
		}else{
			if(null != $conditions)$where = "WHERE ".$conditions;
		}
		if(null != $sort){
			$sort = "ORDER BY {$sort}";
		}else{
			$sort = "ORDER BY {$this->pk}";
		}
		$sql = "SELECT {$fields} FROM {$this->tbl_name} {$this->table} {$where} {$sort}";
		if(null != $limit)$sql = $this->_db->setlimit($sql, $limit);
		return $sql;
	}
	
	public function findJoin($conditions = null, $sort = null, $fields = null){
		if( $record = $this->findAllJoin($conditions, $sort, $fields, 1) ){
			return array_pop($record);
		}else{
			return FALSE;
		}
	}
	
	public function findAllJoin($conditions = null, $sort = null, $fields = null, $limit = null){
		$where_array = array();
		$fields = empty($fields) ? "*" : $fields;
		$leftjoin_table = array();
		$leftjoin_condition = array();
		if(is_array($conditions)){
			$join = array();
			foreach( $conditions as $key => $condition ){
				if(is_array($condition)){
					$cs = array();
					foreach( $condition as $c ){
						$c = $this->escape($c);
						$cs[] = $c;
					}
					$condition = join(",", $cs);
					$join[] = "{$this->table}.{$key} IN ({$condition})";
				}else {
					$condition = $this->escape($condition);
					$join[] = "{$this->table}.{$key} = {$condition}";
				}
			}
			if(count($join)>0)
				$where_array[] = '('.join(" AND ",$join).')';
		}else{
			if(null != $conditions)$where_array[] = '('.$conditions.')';
		}
	
		if(is_array($this->linker)){
			foreach( $this->linker as $onelinker ){
				if($onelinker['enabled']){
					$table_name = $this->dbpre?$this->dbpre.$onelinker['ftable']:$onelinker['ftable'];
					$leftjoin_table[] = "LEFT JOIN {$table_name} {$onelinker['map']} ON {$onelinker['map']}.{$onelinker['fkey']}={$this->table}.{$onelinker['mapkey']}";
					if($sub_conditions = $onelinker['condition']){
						if(is_array($sub_conditions)){
							$sub_condition_join = array();
							foreach( $sub_conditions as $key => $sub_condition ){
								$sub_condition = $this->escape($sub_condition);
								$sub_condition_join[] = "{$onelinker['map']}.{$key} = {$sub_condition}";
							}
							if(count($sub_condition_join)>0)
								$leftjoin_condition[] = '('.join(" AND ",$sub_condition_join).')';
						}else{
							if(null != $sub_conditions) $leftjoin_condition[] = '('.$sub_conditions.')';
						}
					}
				}
			}
		}
	
	
		if(is_array($this->other_join)){
			foreach( $this->other_join as $other ){
				if($other&&$other['enabled']){
					$leftjoin_table[] = "LEFT JOIN {$this->dbpre}{$other['main_table']} {$other['main_alias']} ON {$other['main_alias']}.{$other['main_fkey']}={$other['sec_table_alias']}.{$other['sec_mapkey']}";
				}
			}
		}
	
		$leftjoin = (count($leftjoin_table)>0)?join(' ',$leftjoin_table):'';
		
		if(count($leftjoin_condition)>0){
			$where_array[] = '('.join(" AND ",$leftjoin_condition).')';
		}
		$where = (count($where_array)>0)?"WHERE ".join(" AND ",$where_array):'';
		$sort = ($sort==null)?"ORDER BY {$this->pk}":"ORDER BY {$sort}";
		$sql = "SELECT {$fields} FROM {$this->tbl_name} {$this->table} {$leftjoin} {$where} {$sort}";
		if(null != $limit)$sql = $this->_db->setlimit($sql, $limit);
		return $this->_db->getArray($sql);
	}
	
	public function findCount($conditions = null){
		$where = "";
		if(is_array($conditions)){
			$join = array();
			foreach( $conditions as $key => $condition ){
				$condition = $this->escape($condition);
				$join[] = "{$key} = {$condition}";
			}
			$where = "WHERE ".join(" AND ",$join);
		}else{
			if(null != $conditions)$where = "WHERE ".$conditions;
		}
		$sql = "SELECT COUNT({$this->pk}) AS PZ_NUMBER FROM {$this->tbl_name} {$this->table} {$where}";
		$result = $this->_db->getArray($sql);
		return $result[0]['PZ_NUMBER'];
	}
	
	public function findCountJoin($conditions = null){
		$where_array = array();
		$leftjoin_table = array();
		$leftjoin_condition = array();
		if(is_array($conditions)){
			$join = array();
			foreach( $conditions as $key => $condition ){
				$condition = $this->escape($condition);
				$join[] = "{$this->table}.{$key} = {$condition}";
			}
			if(count($join)>0)
				$where_array[] = '('.join(" AND ",$join).')';
		}else{
			if(null != $conditions)$where_array[] = '('.$conditions.')';
		}
	
		if(is_array($this->linker)){
			$leftjoin_table = array();
			$leftjoin_condition = array();
			foreach( $this->linker as $onelinker ){
				if($onelinker['enabled']){
					$table_name = $this->dbpre?$this->dbpre.$onelinker['ftable']:$onelinker['ftable'];
					$leftjoin_table[] = "LEFT JOIN {$table_name} {$onelinker['map']} ON {$onelinker['map']}.{$onelinker['fkey']}={$this->table}.{$onelinker['mapkey']}";
					if($sub_conditions = $onelinker['condition']){
						if(is_array($sub_conditions)){
							$sub_condition_join = array();
							foreach( $sub_conditions as $key => $sub_condition ){
								$sub_condition = $this->escape($sub_condition);
								$sub_condition_join[] = "{$onelinker['map']}.{$key} = {$sub_condition}";
							}
							if(count($sub_condition_join)>0)
								$leftjoin_condition[] = '('.join(" AND ",$sub_condition_join).')';
						}else{
							if(null != $sub_conditions) $leftjoin_condition[] = '('.$sub_conditions.')';
						}
					}
				}
			}
		}
	
		if(is_array($this->other_join)){
			foreach( $this->other_join as $other ){
				if($other&&$other['enabled']){
					$leftjoin_table[] = "LEFT JOIN {$this->dbpre}{$other['main_table']} {$other['main_alias']} ON {$other['main_alias']}.{$other['main_fkey']}={$other['sec_table_alias']}.{$other['sec_mapkey']}";
				}
			}
		}
	
		$leftjoin = (count($leftjoin_table)>0)?join(' ',$leftjoin_table):'';
		if(count($leftjoin_condition)>0){
			$where_array[] = '('.join(' AND ',$leftjoin_condition).')';
		}
		$where = (count($where_array)>0)?'WHERE '.join(' AND ',$where_array):'';
		$sql = "SELECT COUNT({$this->table}.{$this->pk}) AS PZ_NUMBER FROM {$this->tbl_name} {$this->table} {$leftjoin} {$where}";
		$result = $this->_db->getArray($sql);
		return $result[0]['PZ_NUMBER'];
	}
	
	public function createAll($rows){
		foreach($rows as $row)$this->create($row);
	}
	
	public function delete($conditions){
		$where = "";
		if(is_array($conditions)){
			$join = array();
			foreach( $conditions as $key => $condition ){
				$condition = $this->escape($condition);
				$join[] = "{$key} = {$condition}";
			}
			$where = "WHERE ( ".join(" AND ",$join). ")";
		}else{
			if(null != $conditions)$where = "WHERE ( ".$conditions. ")";
		}
		$sql = "DELETE FROM {$this->tbl_name} {$where}";
		return $this->_db->query($sql);
	}
	
	public function findBy($field, $value){
		return $this->find(array($field=>$value));
	}

	public function deleteBy($field, $value){
		return $this->delete(array($field=>$value));
	}
	
	public function updateField($conditions, $field, $value){
		return $this->update($conditions, array($field=>$value));
	}
	
	public function create($row){
		if(!is_array($row))return FALSE;
		$row = $this->__prepera_format($row);
		if(empty($row)) return FALSE;
		foreach($row as $key => $value){
			$cols[] = $key;
			$vals[] = $this->escape($value);
		}
		$col = join(',', $cols);
		$val = join(',', $vals);
	
		$sql = "INSERT INTO {$this->tbl_name} ({$col}) VALUES ({$val})";
		if( FALSE != $this->_db->query($sql) ){
			if( $newinserid = $this->_db->insertId() ){
				return $newinserid;
			}else{
				return array_pop( $this->find($row, "{$this->pk} DESC",$this->pk) );
			}
		}
		return FALSE;
	}
	
	public function update($conditions, $row){
		$where = "";
		$row = $this->__prepera_format($row);
		if(empty($row))return FALSE;
		if(is_array($conditions)){
			$join = array();
			foreach( $conditions as $key => $condition ){
				$condition = $this->escape($condition);
				$join[] = "{$key} = {$condition}";
			}
			$where = "WHERE ".join(" AND ",$join);
		}else{
			if(null != $conditions)$where = "WHERE ".$conditions;
		}
		foreach($row as $key => $value){
			$value = $this->escape($value);
			$vals[] = "{$key} = {$value}";
		}
		$values = join(", ",$vals);
		$sql = "UPDATE {$this->tbl_name} SET {$values} {$where}";
		return $this->_db->query($sql);
	}
	
	public function replace($conditions, $row){
		if( $this->find($conditions) ){
			return $this->update($conditions, $row);
		}else{
			if( !is_array($conditions) ) return;
			$rows = combineArray($conditions, $row);
			return $this->create($rows);
		}
	}

	public function findSql($sql){
		return $this->_db->getArray($sql);
	}

	public function runSql($sql){
		return $this->_db->query($sql);
	}
	public function incrField($conditions, $field, $optval = 1){
		$where = "";
		if(is_array($conditions)){
			$join = array();
			foreach( $conditions as $key => $condition ){
				$condition = $this->escape($condition);
				$join[] = "{$key} = {$condition}";
			}
			$where = "WHERE ".join(" AND ",$join);
		}else{
			if(null != $conditions)$where = "WHERE ".$conditions;
		}
		$values = "{$field} = {$field} + {$optval}";
		$sql = "UPDATE {$this->tbl_name} SET {$values} {$where}";
		return $this->_db->query($sql);
	}
	
	public function decrField($conditions, $field, $optval = 1){
		return $this->incrField($conditions, $field, - $optval);
	}
	
	public static function quote($str, $noarray = false) {
		if (is_string($str))
			return '\'' . addcslashes($str, "\n\r\\'\"\032") . '\'';
		if (is_int($str) or is_float($str))
			return '\'' . $str . '\'';
		if (is_array($str)) {
			if($noarray === false) {
				foreach ($str as &$v) {
					$v = self::quote($v, true);
				}
				return $str;
			} else {
				return '\'\'';
			}
		}
		if (is_bool($str))
			return $str ? '1' : '0';
		return '\'\'';
	}
	
	public function deleteByPk($pk){
		return $this->delete(array($this->pk=>$pk));
	}
	
	
	public function escape($value){
		return $this->_db->__escape($value);
	}
	
	private function __prepera_format($rows){
		$columns = $this->_db->getTable($this->tbl_name);
		$newcol = array();
		foreach( $columns as $col ){
			$newcol[$col['Field']] = $col['Field'];
		}
		return array_intersect_key($rows,$newcol);
	}
	
	public static function factory($params) {
		switch ($params['dbdriver']) {
			case 'mysql':
				pimport('db/db_mysql.php',SYSPATH);
				$linkId = db_mysql::getInstance($params);
				break;
			default:
				$linkId = db_mysqli::getInstance($params);
		}
		return $linkId;
	}
	
	public function pager($page,$pagesize){
		$pager = & load_class('Pager', 'core');
		return $pager->init($this,$page,$pagesize);
	}
}
